home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / VideoFolder 1.0a / Source / MoreFiles 1.4.1 / DirectoryCopy.c < prev    next >
Text File  |  1995-12-21  |  16KB  |  454 lines

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    DirectoryCopy: A robust, general purpose directory copy routine.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        DirectoryCopy.c
  9. **
  10. **    Copyright © 1992-1995 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Errors.h>
  24. #include <Memory.h>
  25. #include <Files.h>
  26. #include "MoreFiles.h"
  27. #include "MoreFilesExtras.h"
  28. #include "MoreDesktopMgr.h"
  29. #include "FileCopy.h"
  30. #include "DirectoryCopy.h"
  31.  
  32. /*****************************************************************************/
  33.  
  34. /* local constants */
  35.  
  36. enum
  37. {
  38.     dirCopyBigCopyBuffSize  = 0x00004000,
  39.     dirCopyMinCopyBuffSize  = 0x00000200
  40. };
  41.  
  42.  
  43. /*****************************************************************************/
  44.  
  45. /* local data structures */
  46.  
  47. /* The EnumerateGlobals structure is used to minimize the amount of
  48. ** stack space used when recursively calling CopyLevel and to hold
  49. ** global information that might be needed at any time. */
  50.  
  51. #if PRAGMA_ALIGN_SUPPORTED
  52. #pragma options align=mac68k
  53. #endif
  54. struct EnumerateGlobals
  55. {
  56.     Ptr            copyBuffer;            /* pointer to buffer used for file copy operations */
  57.     long        bufferSize;            /* the size of the copy buffer */
  58.     CopyErrProcPtr errorHandler;    /* pointer to error handling function */
  59.     OSErr        error;                /* temporary holder of results - saves 2 bytes of stack each level */
  60.     Boolean        bailout;            /* set to true to by error handling function if fatal error */
  61.     short        destinationVRefNum;    /* the destination vRefNum */
  62.     Str63        itemName;            /* the name of the current item */
  63.     CInfoPBRec    myCPB;                /* the parameter block used for PBGetCatInfo calls */
  64. };
  65. #if PRAGMA_ALIGN_SUPPORTED
  66. #pragma options align=reset
  67. #endif
  68.  
  69. typedef struct EnumerateGlobals EnumerateGlobals;
  70. typedef EnumerateGlobals *EnumerateGlobalsPtr;
  71.  
  72.  
  73. /* The PreflightGlobals structure is used to minimize the amount of
  74. ** stack space used when recursively calling GetLevelSize and to hold
  75. ** global information that might be needed at any time. */
  76.  
  77. #if PRAGMA_ALIGN_SUPPORTED
  78. #pragma options align=mac68k
  79. #endif
  80. struct PreflightGlobals
  81. {
  82.     OSErr            result;                /* temporary holder of results - saves 2 bytes of stack each level */
  83.     Str63            itemName;            /* the name of the current item */
  84.     CInfoPBRec        myCPB;                /* the parameter block used for PBGetCatInfo calls */
  85.  
  86.     unsigned short    dstBlksPerAllocBlk;    /* the number of 512 byte blocks per allocation block on destination */
  87.     unsigned short    allocBlksNeeded;    /* the total number of allocation blocks needed  */
  88.  
  89.     unsigned short    tempBlocks;            /* temporary storage for calculations (save some stack space)  */
  90. };
  91. #if PRAGMA_ALIGN_SUPPORTED
  92. #pragma options align=reset
  93. #endif
  94.  
  95. typedef struct PreflightGlobals PreflightGlobals;
  96. typedef PreflightGlobals *PreflightGlobalsPtr;
  97.  
  98. /*****************************************************************************/
  99.  
  100. /* static prototypes */
  101.  
  102. static    void    GetLevelSize(long currentDirID,
  103.                              PreflightGlobalsPtr theGlobals);
  104.  
  105. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  106.                                             long srcDirID,
  107.                                             short dstVRefNum,
  108.                                             Boolean *spaceOK);
  109.  
  110. static    void    CopyLevel(long sourceDirID,
  111.                           long dstDirID,
  112.                           EnumerateGlobalsPtr theGlobals);
  113.                           
  114. /*****************************************************************************/
  115.  
  116. static    void    GetLevelSize(long currentDirID,
  117.                              PreflightGlobalsPtr theGlobals)
  118. {
  119.     short    index = 1;
  120.     
  121.     do
  122.     {
  123.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  124.         theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID;    /* we need to do this every time */
  125.                                                             /* through, since GetCatInfo  */
  126.                                                             /* returns ioFlNum in this field */
  127.         theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);
  128.         if ( theGlobals->result == noErr )
  129.         {
  130.             if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  131.             {
  132.                 /* we have a directory */
  133.                 
  134.                 GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
  135.                 theGlobals->result = noErr; /* clear error return on way back */
  136.             }
  137.             else
  138.             {
  139.                 /* we have a file - add its allocation blocks to allocBlksNeeded */
  140.                 
  141.                 /* get number of 512-byte blocks needed for data fork */
  142.                 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen % 512) ?
  143.                                             (((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1) :
  144.                                             ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9);
  145.                 /* now, calculate number of new allocation blocks needed */
  146.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  147.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  148.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  149.                 
  150.                 /* get number of 512-byte blocks needed for resource fork */
  151.                 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen % 512) ?
  152.                                             (((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1) :
  153.                                             ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9);
  154.                 /* now, calculate number of new allocation blocks needed */
  155.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  156.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  157.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  158.             }
  159.         }
  160.         ++index;
  161.     } while ( theGlobals->result == noErr );
  162. }
  163.  
  164. /*****************************************************************************/
  165.  
  166. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  167.                                             long srcDirID,
  168.                                             short dstVRefNum,
  169.                                             Boolean *spaceOK)
  170. {
  171.     HParamBlockRec pb;
  172.     OSErr error;
  173.     unsigned short dstFreeBlocks;
  174.     PreflightGlobals theGlobals;
  175.     
  176.     /* Get the number of 512 byte blocks per allocation block and */
  177.     /* number of free allocation blocks on the destination volume */
  178.     pb.volumeParam.ioVRefNum = dstVRefNum;
  179.     pb.volumeParam.ioNamePtr = NULL;
  180.     pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  181.     error = PBHGetVInfoSync(&pb);
  182.     if ( error == noErr )
  183.     {
  184.         dstFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
  185.         
  186.         /* get allocation block size (always multiple of 512) and divide by 512
  187.           to get number of 512-byte blocks per allocation block */
  188.         theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.volumeParam.ioVAlBlkSiz >> 9);
  189.         
  190.         theGlobals.allocBlksNeeded = 0;
  191.  
  192.         theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
  193.         theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
  194.         
  195.         
  196.         GetLevelSize(srcDirID, &theGlobals);
  197.         
  198.         /* Is there enough room on the destination volume for the source file? */
  199.         *spaceOK = (theGlobals.allocBlksNeeded <= dstFreeBlocks);
  200.     }
  201.     return ( error );
  202. }
  203.  
  204. /*****************************************************************************/
  205.  
  206. static    void    CopyLevel(long sourceDirID,
  207.                           long dstDirID,
  208.                           EnumerateGlobalsPtr theGlobals)
  209. {
  210.     long currentSrcDirID;
  211.     long newDirID;
  212.     short index = 1;
  213.     
  214.     do
  215.     {    /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
  216.     
  217.         /* Get next source item at the current directory level */
  218.         
  219.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  220.         theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
  221.         theGlobals->error = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);        
  222.  
  223.         if ( theGlobals->error == noErr )
  224.         {
  225.             /* We have an item.  Is it a file or directory? */
  226.             if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  227.             {
  228.                 /* we have a directory */
  229.                 
  230.                 /* create a new directory at the destination. No errors allowed! */
  231.                 theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
  232.                 
  233.                 if ( theGlobals->error == noErr )
  234.                 {
  235.                     /* Save the current source directory ID where we can get it when we come back
  236.                     ** from recursion land. */
  237.                     
  238.                     currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
  239.                     
  240.                     /* Dive again (copy the directory level we just found below this one) */
  241.                     
  242.                     CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
  243.                     
  244.                     if ( !theGlobals->bailout )
  245.                     {
  246.                         /* Copy comment from old to new directory. */
  247.                         /* Ignore the result because we really don't care if it worked or not. */
  248.                         (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
  249.                     }
  250.  
  251.                     if ( !theGlobals->bailout )
  252.                     {
  253.                         /* Copy directory attributes (dates, etc.) to newDirID. */
  254.                         
  255.                         theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
  256.                         
  257.                         /* handle any errors from CopyFileMgrAttributes */
  258.                         if ( (theGlobals->error != noErr) && (theGlobals->errorHandler != NULL) )
  259.                             theGlobals->bailout =  CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
  260.                                                     theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  261.                                                     theGlobals->destinationVRefNum, newDirID, NULL);
  262.                     }
  263.                 }
  264.                 else    /* error handling for DirCreate */
  265.                     if ( theGlobals->errorHandler != NULL )
  266.                         theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
  267.                                                     theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  268.                                                     theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
  269.                 theGlobals->error = noErr;    /* clear error return on way back */
  270.             }
  271.             else
  272.             {
  273.             
  274.                 /* We have a file, so copy it */
  275.                 
  276.                 theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
  277.                                              theGlobals->myCPB.hFileInfo.ioFlParID,
  278.                                              theGlobals->itemName,
  279.                                              theGlobals->destinationVRefNum,
  280.                                              dstDirID,
  281.                                              NULL,
  282.                                              NULL,
  283.                                              theGlobals->copyBuffer,
  284.                                              theGlobals->bufferSize,
  285.                                              false);
  286.                         
  287.                 /* handle any errors from FileCopy */
  288.                 if ( (theGlobals->error != noErr) && (theGlobals->errorHandler != NULL) )
  289.                     theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
  290.                                             theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
  291.                                             theGlobals->destinationVRefNum, dstDirID, NULL);
  292.             }
  293.         }
  294.         else
  295.         {    /* error handling for PBGetCatInfo */
  296.             /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
  297.             if ( (theGlobals->error != fnfErr) && (theGlobals->errorHandler != NULL) )
  298.             { 
  299.                 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
  300.                                         theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
  301.             }
  302.         }
  303.         
  304.         ++index; /* prepare to get next item */
  305.     } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
  306. }
  307.  
  308. /*****************************************************************************/
  309.  
  310. pascal    OSErr    DirectoryCopy(short srcVRefNum,
  311.                               long srcDirID,
  312.                               StringPtr srcName,
  313.                               short dstVRefNum,
  314.                               long dstDirID,
  315.                               StringPtr dstName,
  316.                               void *copyBufferPtr,
  317.                               long copyBufferSize,
  318.                               Boolean preflight,
  319.                               CopyErrProcPtr copyErrHandler)
  320. {
  321.     EnumerateGlobals theGlobals;
  322.     Boolean    isDirectory;
  323.     OSErr    error;
  324.     Boolean ourCopyBuffer = false;
  325.     Str63    srcDirName;
  326.     Boolean spaceOK;
  327.     
  328.     /* Make sure a copy buffer is allocated. */
  329.     if ( copyBufferPtr == NULL )
  330.     {
  331.         /* The caller didn't supply a copy buffer so grab one from the application heap.
  332.         ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
  333.         ** If 512 bytes aren't available, we're in trouble. */
  334.         copyBufferSize = dirCopyBigCopyBuffSize;
  335.         copyBufferPtr = NewPtr(copyBufferSize);
  336.         if ( copyBufferPtr == NULL )
  337.         {
  338.             copyBufferSize = dirCopyMinCopyBuffSize;
  339.             copyBufferPtr = NewPtr(copyBufferSize);
  340.             if ( copyBufferPtr == NULL )
  341.                 return ( memFullErr );
  342.         }
  343.         ourCopyBuffer = true;
  344.     }
  345.     
  346.     /* Get the real dirID where we're copying from and make sure it is a directory. */
  347.     error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
  348.     if ( error != noErr )
  349.         goto ErrorExit;
  350.     if ( !isDirectory )
  351.     {
  352.         error = dirNFErr;
  353.         goto ErrorExit;
  354.     }
  355.         
  356.     /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
  357.     error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
  358.     if ( error != noErr )
  359.         goto ErrorExit;
  360.     if ( !isDirectory )
  361.     {
  362.         error =  dirNFErr;
  363.         goto ErrorExit;
  364.     }
  365.     
  366.     /* Get the real vRefNum of both the source and destination */
  367.     error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
  368.     if ( error != noErr )
  369.         goto ErrorExit;
  370.     error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
  371.     if ( error != noErr )
  372.         goto ErrorExit;
  373.     
  374.     if ( preflight )
  375.     {
  376.         error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, &spaceOK);
  377.         if ( error != noErr )
  378.             goto ErrorExit;
  379.         if ( !spaceOK )
  380.         {
  381.             error = dskFulErr; /* not enough room on destination */
  382.             goto ErrorExit;
  383.         }
  384.     }
  385.  
  386.     /* Create the new directory in the destination directory with the */
  387.     /* same name as the source directory. */
  388.     error = GetDirName(srcVRefNum, srcDirID, srcDirName);
  389.     if ( error != noErr )
  390.         goto ErrorExit;
  391.     error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
  392.     if ( error != noErr )
  393.         goto ErrorExit;
  394.     
  395.     /* dstDirID is now the newly created directory! */
  396.         
  397.     /* Set up the globals we need to access from the recursive routine. */
  398.     theGlobals.copyBuffer = (Ptr)copyBufferPtr;
  399.     theGlobals.bufferSize = copyBufferSize;
  400.     theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
  401.     theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
  402.     theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
  403.     theGlobals.errorHandler = copyErrHandler;
  404.     theGlobals.bailout = false;
  405.         
  406.     /* Here we go into recursion land... */
  407.     CopyLevel(srcDirID, dstDirID, &theGlobals);
  408.     
  409.     if ( !theGlobals.bailout )
  410.     {
  411.         /* Copy comment from source to destination directory. */
  412.         /* Ignore the result because we really don't care if it worked or not. */
  413.         (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
  414.     }
  415.  
  416.     if ( !theGlobals.bailout )
  417.     {
  418.         /* Copy the File Manager attributes */
  419.         
  420.         error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
  421.                     dstVRefNum, dstDirID, NULL, true);
  422.         
  423.         /* handle any errors from CopyFileMgrAttributes */
  424.         if ( (error != noErr) && (copyErrHandler != NULL) )
  425.             theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
  426.                                                 srcVRefNum, srcDirID, NULL,
  427.                                                 dstVRefNum, dstDirID, NULL);
  428.     }
  429.  
  430. ErrorExit:
  431.     /* Get rid of the copy buffer if we allocated it. */
  432.     if ( ourCopyBuffer )
  433.         DisposePtr((Ptr)copyBufferPtr);
  434.  
  435.     return ( error );
  436. }
  437.  
  438. /*****************************************************************************/
  439.  
  440. pascal    OSErr    FSpDirectoryCopy(const FSSpec *srcSpec,
  441.                                  const FSSpec *dstSpec,
  442.                                  void *copyBufferPtr,
  443.                                  long copyBufferSize,
  444.                                  Boolean preflight,
  445.                                  CopyErrProcPtr copyErrHandler)
  446. {
  447.     return ( DirectoryCopy(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  448.                           dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name,
  449.                           copyBufferPtr, copyBufferSize, preflight, copyErrHandler) );
  450. }
  451.  
  452. /*****************************************************************************/
  453.  
  454.